package com.gf.framework.redis.config;

import com.gf.framework.common.json.GfJsonMapper;
import com.gf.framework.redis.core.RedisClient;
import com.gf.framework.redis.cache.ExpireRedisCacheWriter;
import com.gf.framework.redis.properties.RedisCacheExpireProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * RedisCache相关配置
 *
 * @author gaofei
 */
@EnableCaching
@Configuration
@Import(RedisClient.class)
public class RedisCacheAutoConfiguration extends CachingConfigurerSupport {
    @Value("${spring.application.name:unknown}")
    private String appName;

    @Autowired
    RedisCacheExpireProperties redisCacheExpireProperties;

    /**
     * 注入RedisTemplate
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(
            RedisConnectionFactory factory,
            GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer) {
        // 指定序列化方式
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }

    /**
     * 配置使用@Cachable在redis生成的key的规则
     * <p>
     * 格式：应用名:类名.方法(入参列表)
     * 示例：order:OrderService.getOrderById(1111)
     */
    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        if (redisCacheExpireProperties.getSimpleKey()) {
            return (target, method, params) ->
                    Stream.of(params).map(Object::toString).collect(Collectors.joining(",", "(", ")"));
        }
        else {
            return (target, method, params) -> {
                String paramStr = Stream.of(params).map(Object::toString).collect(Collectors.joining(",", "(", ")"));
                return String.format("%s:%s:%s:%s", appName, target.getClass().getSimpleName(), method.getName(), paramStr);
            };
        }
    }

    /**
     * 配置使用@Cachable时在redis中序列化的方式
     */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer) {
        RedisSerializationContext.SerializationPair<Object> pair =
                RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer);
        return RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
    }

    /**
     * redis 默认序列化方式
     * @return 默认序列化方式
     */
    @Bean
    public GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer() {
        return new GenericJackson2JsonRedisSerializer(GfJsonMapper.getObjectMapperWithType());
    }

    @Bean
    ExpireRedisCacheWriter expireRedisCacheWriter(
            RedisConnectionFactory connectionFactory,
            RedisCacheExpireProperties redisCacheExpireProperties
    ) {
        return new ExpireRedisCacheWriter(connectionFactory, redisCacheExpireProperties);
    }

    /**
     * 使用自定义的ExpireRedisCacheWriter完成一些注解和redis cache超时配置
     * @param expireRedisCacheWriter 自定义的ExpireRedisCacheWriter
     * @param redisCacheConfiguration 默认配置
     * @return
     */
    @Bean
    public CacheManager redisCacheManager(
            ExpireRedisCacheWriter expireRedisCacheWriter,
            RedisCacheConfiguration redisCacheConfiguration) {
        return new RedisCacheManager(expireRedisCacheWriter, redisCacheConfiguration);
    }
}
